﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using X3DXNA.customTypes;
using System.Windows.Data;
using X3DXNA.core.Interpolators;
using Microsoft.Xna.Framework;
using X3DXNA.interfaces;
using X3DXNA.X3DSchema;
using X3DXNA.utils;

namespace X3DXNA.core.Sensors
{
    class MyTimeSensor : IRoutable
    {

        #region Private Fields

        float oldFraction;

        bool TimeSensorIsActive = false;
        bool TimeSensorIsInPause = false;
        float? fraction;

        #endregion

        #region Public Properties

        public String DEF { get; set; }
        public bool enabled { get; set; }
        public SFTime cycleInterval { get; set; }
        public bool loop { get; set; }
        public SFTime startTime { get; set; }
        public SFTime stopTime { get; set; }
        public SFTime pauseTime { get; set; }
        public SFTime resumeTime { get; set; }
        public SFTime cycleTime { get; private set; }
        public float? fraction_changed 
        { 
            get 
            {
                if (fraction != null)
                    return (float)fraction;
                else
                    return null;
            } 
        }
        public SFTime time { get { return SFTime.Now; } }

        #endregion

        #region Delegates

        public delegate void DateTimeType(SFTime dateTimeType);
        public delegate void BoolType(bool boolType);
        public delegate void FloatType(float floatType);

        #endregion

        #region Events

        /// <summary>
        /// cycleTime spedisce un output temporale al tempo startTime, e anche all’inizio di ogni nuovo ciclo (utile per la sincronizzazione con altri oggetti che usano il tempo).
        /// </summary>
        public event DateTimeType cycleTime_event;
        /// <summary>
        /// Eventi isActive true/false vengono spediti quando il TimeSensor parte/si ferma.
        /// </summary>
        public event BoolType isActive_event;
        /// <summary>
        /// Eventi isPaused true/false vengono spediti quando il TimeSensor e' in pausa/riparte. 
        /// </summary>
        public event BoolType isPaused_event;
        /// <summary>
        /// fraction_changed spedisce in modo continuo un valore nell’intervallo [0,1] che mostra l’avanzamento del tempo nel ciclo di tempo corrente.
        /// </summary>
        public event FloatType fraction_changed_event;
        /// <summary>
        /// Time spedisce in modo continuo il tempo assoluto (dal 1 Gennaio, 1970).
        /// </summary>
        public event DateTimeType time_event;

        #endregion

        #region Constructor (Default TimeSensor)

        public MyTimeSensor(String DEF, bool enabled, SFTime cycleInterval, bool loop, SFTime startTime, 
                            SFTime stopTime, SFTime pauseTime, SFTime resumeTime)
        {
            this.DEF = DEF;
            this.enabled = enabled;
            this.cycleInterval = cycleInterval;
            this.loop = loop;
            this.startTime = startTime;
            this.stopTime = stopTime;
            this.pauseTime = pauseTime;
            this.resumeTime = resumeTime;

            fraction = null;
        }

        #endregion

        #region Public Initialize

        /// <summary>
        /// Inizializza il timersensor. Da eseguire una volta che la scena X3D è stata computata.
        /// </summary>
        public void Initialize()
        {
            if (loop == true && startTime >= stopTime)
                startTime = SFTime.Now;
        }

        #endregion

        #region Public Update method

        /// <summary>
        /// Update the TimeSensor
        /// </summary>
        public void Update()
        {
            SFTime now = SFTime.Now;
            //Innanzitutto controlliamo se il sensore è abilitato.
            if (enabled)
            {
                //Se il sensore non è attivo, lo startTime è diverso dal valore di default 0, e il sensore non è mai stato
                //attivato da uno startTime, 
                if (!TimeSensorIsActive && startTime > 0 && now >= startTime)
                {
                    TimeSensorIsActive = true;
                    //OnIsActive(true);
                    //OnCycleTime(now);
                    cycleTime = SFTime.Now;
                }

                //Se il sensore è attivo...
                if (TimeSensorIsActive)
                {
                    //...controlliamo se è in pausa e se deve uscirne...
                    if (TimeSensorIsInPause && pauseTime <= now)
                    {
                        TimeSensorIsInPause = false;
                        //OnIsPaused(false);
                    }

                    //...e se non è in pausa...
                    if (!TimeSensorIsInPause)
                    {
                        //Starts to generate events.
                        //OnTimeChanged(now);

                        if (!loop)
                        {
                            if ((now - startTime) > cycleInterval)
                            {
                                TimeSensorIsActive = false;
                                //OnIsActive(false);
                            }
                            else
                            {
                                float fraction = (now - startTime) / cycleInterval;
                                //OnFractionChanged(fraction);
                                this.fraction = fraction;
                            }
                        }
                        else
                        {
                            float fraction = ((now - startTime) % cycleInterval) / cycleInterval;

                            if (fraction < oldFraction)
                            {
                                //OnCycleTime(now);
                                cycleTime = SFTime.Now;
                            }

                            oldFraction = fraction;
                            //OnFractionChanged(fraction);
                            this.fraction = fraction;
                        }

                        //Controlliamo se il sensore deve stopparsi.
                        if (TimeSensorIsActive && stopTime > startTime && stopTime <= now)
                        {
                            TimeSensorIsActive = false;
                            startTime = new SFTime(0);
                            //OnIsActive(false);
                        }

                        //Controlliamo se il sensore deve andare in pausa.
                        if (pauseTime != 0 && TimeSensorIsActive && now >= pauseTime)
                        {
                            TimeSensorIsInPause = true;
                            //OnIsPaused(true);
                        }

                    }
                }
            }
        }

        #endregion

        #region Events Method

        void OnCycleTime(SFTime dateTime)
        {
            if (cycleTime_event != null)
                cycleTime_event(dateTime);
        }

        void OnIsActive(bool Bool)
        {
            if (isActive_event != null)
                isActive_event(Bool);
        }

        void OnIsPaused(bool Bool)
        {
            if (isPaused_event!= null)
                isPaused_event(Bool);
        }

        void OnFractionChanged(float floatValue)
        {
            if (fraction_changed_event != null)
                fraction_changed_event(floatValue);
        }

        void OnTimeChanged(SFTime dateTime)
        {
            if (time_event != null)
                time_event(dateTime);
        }

        #endregion

        #region Public Static Builder

        public static MyTimeSensor getNewMyTimerSensor(TimeSensor ts, SFTime creationTime)
        {
            String DEF = "Default TimerSensor";
            bool enabled = true;
            SFTime cycleInterval = new SFTime(1);
            bool loop = false;
            SFTime startTime = new SFTime(0);
            SFTime stopTime = new SFTime(0);
            SFTime pauseTime = new SFTime(0);
            SFTime resumeTime = new SFTime(0);

            if (ts.DEF != null)
                DEF = ts.DEF;
            if (ts.enabled != null)
                enabled = utility.stringToBool(ts.enabled);
            if (ts.cycleInterval != null)
                cycleInterval = new SFTime(utility.stringToFloat(ts.cycleInterval));
            if (ts.loop != null)
                loop = utility.stringToBool(ts.loop);
            if (ts.startTime != null)
                startTime = new SFTime(utility.stringToFloat(ts.startTime));
            if (ts.stopTime != null)
                stopTime = new SFTime(utility.stringToFloat(ts.stopTime));
            if (ts.pauseTime != null)
                pauseTime = new SFTime(utility.stringToFloat(ts.pauseTime));
            if (ts.resumeTime != null)
                resumeTime = new SFTime(utility.stringToFloat(ts.resumeTime));

            return new MyTimeSensor(DEF, enabled, cycleInterval, loop, startTime, stopTime, pauseTime, resumeTime);
        }

        #endregion

    }

}
